﻿using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Modules.Constants;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CSharp;

namespace System.Modules.Extensions
{
    /// <summary>
    /// String Extensions
    /// </summary>
    public static class StringExtensions
    {
        #region Is Null Or Empty
        /// <summary>
        /// This Method checks if string is Null or Empty
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }
        #endregion

        #region Is Not Null Or Empty
        /// <summary>
        /// This Method checks if string is not Null or Empty
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNotNullOrEmpty(this string value)
        {
            return !string.IsNullOrEmpty(value);
        }
        #endregion

        #region Fix Html Image Src
        /// <summary>
        /// Use this method to replace HTML image source with a prefix.
        /// </summary>
        /// <param name="htmlText"></param>
        /// <param name="urlPrefix"></param>
        /// <returns></returns>
        public static string FixHtmlImageSrc(this string htmlText, string urlPrefix)
        {
            return new StringBuilder(htmlText).FixHtmlImageSrc(urlPrefix).ToString();
        }
        #endregion

        #region Remove Illegal File Name Charachters
        /// <summary>
        /// Use this method to remove all illegal file name characters
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string RemoveIllegalFileNameCharachters(this string fileName)
        {
            return fileName.ReplaceIllegalFileNameCharachters(string.Empty);
        }

        /// <summary>
        /// Use this method to remove all illegal file name characters
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="includeSpaceCharacter"> </param>
        /// <returns></returns>
        public static string RemoveIllegalFileNameCharachters(this string fileName, bool includeSpaceCharacter)
        {
            return fileName.ReplaceIllegalFileNameCharachters(string.Empty, includeSpaceCharacter);
        }
        #endregion

        #region Replace Illegal File Name Charachters
        /// <summary>
        /// Use this method to replace all illegal file name characters with a placeholder 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="placeHolder"> </param>
        /// <returns></returns>
        public static string ReplaceIllegalFileNameCharachters(this string fileName, string placeHolder)
        {
            return fileName.ReplaceIllegalFileNameCharachters(placeHolder, false);
        }

        /// <summary>
        /// Use this method to replace all illegal file name characters with a placeholder 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="placeHolder"> </param>
        /// <param name="includeSpaceCharacter"> </param>
        /// <returns></returns>
        public static string ReplaceIllegalFileNameCharachters(this string fileName, string placeHolder, bool includeSpaceCharacter)
        {
            var invalidCharacters = new StringBuilder();
            invalidCharacters.Append(new string(Path.GetInvalidFileNameChars()));
            invalidCharacters.Append(new string(Path.GetInvalidPathChars()));
            invalidCharacters.Append(new string(FileNameFormat.INVALID_PATH_AND_FILE_NAME_CHARACTERS.ToCharArray()));
            if (includeSpaceCharacter)
            {
                invalidCharacters.Append(FileNameFormat.EMPTY_SPACE);
            }
            var containsABadCharacter = new Regex(string.Format("[{0}]", Regex.Escape(invalidCharacters.ToString())));
            return containsABadCharacter.Replace(fileName, placeHolder);
        }
        #endregion

        #region Remove Spaces
        /// <summary>
        /// Use this method to remove all spaces from a string value
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string RemoveSpaces(this string value)
        {
            return value.ReplaceSpaces(string.Empty);
        }
        #endregion

        #region Replace Spaces
        /// <summary>
        /// Use this method to remove all spaces from a string value
        /// </summary>
        /// <param name="value"></param>
        /// <param name="placeHolder"> </param>
        /// <returns></returns>
        public static string ReplaceSpaces(this string value, string placeHolder)
        {
            return value.IsNotNullOrEmpty() ? Regex.Replace(value, @"\s+", placeHolder) : string.Empty;
        }
        #endregion

        #region Split
        /// <summary>
        /// This Method Split string into a list of Strings
        /// </summary>
        /// <param name="value"></param>
        /// <param name="splitter"></param>
        /// <returns></returns>
        public static List<string> Split(this string value, params string[] splitter)
        {
            return value.Split(StringSplitOptions.RemoveEmptyEntries, splitter);
        }
        /// <summary>
        /// This Method Split string into a list of Strings
        /// </summary>
        /// <param name="value"></param>
        /// <param name="splitOptions"></param>
        /// <param name="splitter"></param>
        /// <returns></returns>
        public static List<string> Split(this string value, StringSplitOptions splitOptions, params string[] splitter)
        {
            var split = new List<string>();
            if (value.IsNotNullOrEmpty())
            {
                var splitArray = value.Split(splitter, splitOptions);
                if (splitArray.Length > 0)
                {
                    split = splitArray.ToList();
                }
            }
            return split;
        }
        #endregion

        #region Set Place Holders Values
        /// <summary>
        /// Sets Place Holders Values
        /// Place Holder Format Template {Type.Property}
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="originalValue"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SetPlaceHoldersValues<T>(this string originalValue, T obj)
        {
            var returnValue = new StringBuilder(originalValue);
            if (!obj.Equals(default(T)))
            {
                var type = typeof(T);
                var properties = type.GetProperties();
                foreach (var property in properties)
                {
                    var propertyName = property.Name;
                    var propertyValue = property.GetValue(obj, null);
                    returnValue.Replace(string.Format("{{{0}.{1}}}", type.Name, propertyName), propertyValue == null ? string.Empty : propertyValue.ToString());
                } 
            }
            return returnValue.ToString();
        }
        #endregion

        #region Build Dynamic Code
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <param name="methodName"> </param>
        /// <param name="directives"> </param>
        /// <param name="namespaceName"> </param>
        /// <param name="typeName"> </param>
        /// <returns></returns>
        public static string BuildDynamicCode(this string code, string namespaceName, string typeName, string methodName, params  string[] directives)
        {
            var builder = new StringBuilder();
            foreach (var directive in directives)
            {
                builder.AppendFormat("using {0};", directive);
            }
            builder.AppendFormat("namespace {0}", namespaceName);
            builder.Append("{");
            builder.AppendFormat("public class {0}", typeName);
            builder.Append("{");
            builder.AppendFormat("public object {0} (params object[] parameters)", methodName);
            builder.Append("{");
            builder.Append(code);
            builder.Append("}");
            builder.Append("}");
            builder.Append("}");
            return builder.ToString();
        }
        #endregion

        #region Compile
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Assembly Compile(this string code)
        {
            if (code.IsNotNullOrEmpty())
            {
                var providerOptions = new Dictionary<string, string> { { "CompilerVersion", "v3.5" } };
                var provider = new CSharpCodeProvider(providerOptions);
                var compilerParameters = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true };
                var results = provider.CompileAssemblyFromSource(compilerParameters, code);
                if (results.Errors.HasErrors)
                {
                    var errors = new StringBuilder("Compiler Errors :\r\n");
                    foreach (CompilerError error in results.Errors)
                    {
                        errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
                    }
                    throw new Exception(errors.ToString());
                }
                return results.CompiledAssembly;
            }
            return null;
        }
        #endregion
    }
}